package com.tyk.bookstore.front.order.strategy.split.impl;


import com.tyk.bookstore.front.common.core.model.dto.NameDto;
import com.tyk.bookstore.front.common.core.util.AssertUtil;
import com.tyk.bookstore.front.common.other.map.MapManager;
import com.tyk.bookstore.front.common.other.map.model.dto.DistanceDto;
import com.tyk.bookstore.front.common.other.map.model.query.DistanceQuery;
import com.tyk.bookstore.front.member.service.DeliveryAddressApiService;
import com.tyk.bookstore.front.order.model.result.OrderResultEnum;
import com.tyk.bookstore.front.order.strategy.split.OrderSplitAction;
import com.tyk.bookstore.front.order.strategy.split.model.SplitDto;
import com.tyk.bookstore.front.order.strategy.split.model.SplitStock;
import com.tyk.bookstore.front.order.util.MapStructOrder;
import com.tyk.bookstore.front.storage.model.dto.StockMeetDto;
import com.tyk.bookstore.front.storage.model.dto.Warehouse4OrderIdDto;
import com.tyk.bookstore.front.storage.service.StockApiService;
import com.tyk.bookstore.front.storage.service.WarehouseApiService;
import com.tyk.bookstore.front.system.service.AreaApiService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author tyk
 * @description ShortDistanceStrictSplitAction
 */
@Component
@RequiredArgsConstructor
public class ShortDistanceStrictSplitAction implements OrderSplitAction {

    private final MapManager mapManager;
    private final DeliveryAddressApiService deliveryAddressApiService;
    private final AreaApiService areaApiService;
    private final StockApiService stockApiService;
    private final WarehouseApiService warehouseApiService;
    private final MapStructOrder mapStructOrder;

    @Override
    public List<SplitDto> split(Long cneeAddId, List<SplitStock> splitStockList) {

        // 严格满足要求的库存记录
        List<StockMeetDto> smListHave = stockApiService.getStockMeetListWithStrict(
                mapStructOrder.toStock4OrderStockMeetQueryList(splitStockList)
        );

        // 检查是否所需的所有书籍均严格满足库存
        long bookCount = smListHave.stream().mapToLong(StockMeetDto::getBookId).distinct().count();
        AssertUtil.equal(bookCount, (long) splitStockList.size(), OrderResultEnum.STOCK_NOT_MEET);

        // warehouseId=>bookIdSet   映射
        Map<Long, Set<Long>> wbsMap = smListHave.stream().collect(
                Collectors.groupingBy(
                        StockMeetDto::getWarehouseId,
                        Collectors.mapping(StockMeetDto::getBookId, Collectors.toSet()
                        )
                )
        );

        // List<(仓库id,区域code)>
        List<Warehouse4OrderIdDto> waIdList = warehouseApiService.getAreaCodeListByIdList(wbsMap.keySet().stream().toList());

        // 区域code=>区域地址 映射
        Map<Long, String> codeMaName = areaApiService
                .getAreaFullNameByCodeSetWithCache(
                        waIdList.stream().map(Warehouse4OrderIdDto::getAreaCode).collect(Collectors.toSet())
                )
                .stream().collect(Collectors.toMap(
                        NameDto::getId,
                        NameDto::getName,
                        (v1, v2) -> v1
                ));

        // 各仓库地址
        List<DistanceQuery> distanceQueryList = waIdList.stream().map(
                it -> new DistanceQuery(it.getWarehouseId(), codeMaName.get(it.getAreaCode()))
        ).toList();

        // 收件地址
        String consigneeAddress = areaApiService.getAreaFullNameByCode(
                deliveryAddressApiService.geAreaCodeById(cneeAddId)
        );

        // 收件地址到各仓库地址的距离
        List<DistanceDto> distanceList = mapManager.calDistance(consigneeAddress, distanceQueryList);

        // 按距离从近到远排序
        distanceList.sort(Comparator.comparing(DistanceDto::getDistance));

        // bookId=>num 映射
        Map<Long, Long> idMapNum = splitStockList.stream().collect(Collectors.toMap(SplitStock::getBookId, SplitStock::getNum));

        // 需要的商品的ID
        Set<Long> bookIdSetNeed = splitStockList.stream().map(SplitStock::getBookId).collect(Collectors.toSet());


        // 从近到远选择仓库
        List<SplitDto> sdList = new ArrayList<>();
        for (DistanceDto d : distanceList) {

            if (!wbsMap.containsKey(d.getId())) continue;

            // 拷贝数据
            Set<Long> bookIdSet = new HashSet<>(wbsMap.get(d.getId()));

            // 商品bookIdSet从仓库d.getId()中发出
            sdList.add(new SplitDto(
                    d.getId(),
                    bookIdSet.stream().map(bookId -> new SplitDto.Stock(bookId, idMapNum.get(bookId))).toList()
            ));

            // 将bookIdSet中的商品从bookIdSetNeed中移除
            bookIdSetNeed.removeAll(bookIdSet);

            // 将bookIdSet中的商品从各仓库候选中移除
            wbsMap.forEach((k, v) -> v.removeAll(bookIdSet));
            // 移除无候选商品的仓库
            wbsMap.values().removeIf(Set::isEmpty);

            // 没有仓库则退出
            if (wbsMap.isEmpty()) break;
        }

        // 返回拆分结果
        return sdList;
    }
}
